package ua.pp.bizon.cripto.keystore;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class AES256PasswordProtectedWithSalt implements ICrypter {

    private static final String SALT2 = new String(AES128.getSert128());
    private byte[] key;

    private AES256PasswordProtectedWithSalt(String username, byte[] cryptedPassword) {
        super();
        try {
            this.key = calculateKey(username, cryptedPassword);
        } catch (Exception e) {
            log.error("failed gnarating key, details: " + e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        } 
    }

    public static ICrypter getCrypter(String login, byte[] password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        return new AES256PasswordProtectedWithSalt(login, password);

    }

    public static byte[] hash(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest sha = MessageDigest.getInstance("SHA-256");
        return sha.digest((password).getBytes("UTF-8"));
    }

    @Override
    public byte[] crypt(String message) {
        return crypt(message.getBytes());
    }

    @Override
    public byte[] crypt(byte[] message) {
        try {
            Cipher cipher = getCrypter(Cipher.ENCRYPT_MODE);

            byte[] encrypted = cipher.doFinal(message);
            return encrypted;
        } catch (Exception e) {
            log.error("error during crypt, detains: " + e.getMessage(), e);
        }
        return null;
    }

    private Cipher getCrypter(int direction) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
        log.trace("key: " + AES128.asHex(key));
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(direction, secretKeySpec);
        return cipher;
    }

    private byte[] calculateKey(String username, byte[] key3) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        byte[] key1 = hash(SALT2);
        byte[] key2 = hash (username);
        
        byte[] key = new byte[key1.length];

        for (int i = 0; i < key1.length; i++) {
            key[i] = (byte) (key2[i] | key1[i] ^ key3[i]);
        }
        return key;
    }

    private final Log log = LogFactory.getLog(getClass());

    @Override
    public byte[] decrypt(byte[] message) throws CryptoException {
        Cipher cipher;
        try {
            cipher = getCrypter(Cipher.DECRYPT_MODE);
            byte[] encrypted = cipher.doFinal(message);
            return encrypted;
        } catch (Exception e) {
            log.warn("wrond password? detains: " + e.getMessage(), e);
            throw new CryptoException("wrond password? detains: " + e.getMessage(), e);
        }
    }

}
